﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using OpenCvSharp;
using Point = OpenCvSharp.Point;

public static class BubbleAreaFinder
{
    public static Rect FindBubbleRoi(Mat gray)
    {
        if (gray.Empty()) return new Rect();

        // 1. 二值化（黑气泡→255）
        using Mat bin = new Mat();
        Cv2.Threshold(gray, bin, 0, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu);

        // 2. 轮廓
        Point[][] contours = Cv2.FindContoursAsArray(bin, RetrievalModes.External,
                                                     ContourApproximationModes.ApproxSimple);

        List<Rect> bubbles = new List<Rect>();
        double imgArea = gray.Width * gray.Height;

        // 3. 面积+圆度过滤
        foreach (var cnt in contours)
        {
            Rect r = Cv2.BoundingRect(cnt);
            double area = Cv2.ContourArea(cnt);
            if (area < imgArea * 0.0002 || area > imgArea * 0.003) continue; // 0.02%~0.3%

            // 圆度 ≈ 4πA/P²，越接近 1 越圆
            double peri = Cv2.ArcLength(cnt, true);
            double circ = 4 * Math.PI * area / (peri * peri + 1e-6);
            if (circ < 0.7) continue;

            bubbles.Add(r);
        }

        if (bubbles.Count < 10) return new Rect();   // 气泡太少直接放弃

        // 4. 垂直栅格密度：把气泡按 y 坐标分桶，统计每 10 px 桶里气泡个数
        int binH = 10;
        int[] yHist = new int[gray.Height / binH + 1];
        foreach (var b in bubbles)
        {
            int idx = b.Y / binH;
            if (idx < yHist.Length) yHist[idx]++;
        }

        // 5. 找连续高密度带（>平均3倍）
        double avg = yHist.Average();
        int start = -1, end = -1;
        for (int i = 0; i < yHist.Length; i++)
        {
            if (yHist[i] > avg * 3)
            {
                if (start < 0) start = i * binH;
                end = (i + 1) * binH;
            }
        }
        if (start < 0) return new Rect();

        // 6. 对保留气泡再做外接矩形
        var keep = bubbles.Where(b => b.Y >= start && b.Y <= end).ToList();
        if (!keep.Any()) return new Rect();

        int x0 = keep.Min(b => b.X);
        int x1 = keep.Max(b => b.Right);
        int y0 = keep.Min(b => b.Y);
        int y1 = keep.Max(b => b.Bottom);

        int pad = (int)(gray.Width * 0.01);   // 留 1% 边距
        return new Rect(
            Math.Max(0, x0 - pad),
            Math.Max(0, y0 - pad),
            Math.Min(gray.Width, x1 - x0 + 2 * pad),
            Math.Min(gray.Height, y1 - y0 + 2 * pad)
        );
    }
}